﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using VideoLink.Frames;
using System.Windows.Forms;

namespace VideoLink.Streams
{
    /// <summary>
    /// Приёмник видео, принимающий видео целиком и воспроизводящий его
    /// с помощью DirectShow
    /// </summary>
    public class EntireDSReceiver : VideoReceiverBase
    {
        #region Fields
		
        /// <summary>
        /// Имя файла, который воспроизводится
        /// </summary>
        private string playFileName = null;
        
        /// <summary>
        /// Компонент, отвечающий за воспроизведение видео
        /// </summary>
        protected DxPlay.DxPlay video;

        /// <summary>
        /// Поток, в который будут добавляться полученные фрагменты.
        /// После завершения приёма всех фрагментов поток будет закрыт
        /// и будет выполнена инициализация видеопроигрывателя
        /// </summary>
        private FileStream playFile;

        private CoolProgressBar progressBar;

	    #endregion

        /// <summary>
        /// Создать новый экземпляр класса по умолчанию
        /// </summary>
        /// <param name="streamID">ID потока, уникален среди всех потоков</param>
        public EntireDSReceiver(int streamID)
            : base(streamID)
        {
        }

        /// <summary>
        /// Создать новый экземпляр класса, который будет обрабатывать 
        /// указанный входящий поток
        /// </summary>
        /// <param name="frame">Кадр, содержащий информацию о видео</param>
        public EntireDSReceiver(VideoStreamInfo frame)
            : this(frame.StreamID)
        {
        }

        /// <summary>
        /// Деструктор
        /// </summary>
        ~EntireDSReceiver()
        {
            Close();
        }

        #region Public methods

        /// <summary>
        /// Обработать новый, только что принятый, кадр
        /// </summary>
        /// <param name="frame">
        /// Информация о кадре. 
        /// Применяется состояние и, если frame содержит VideoFragment, инициализация видео
        /// </param>
        public override void NewFrameReceived(VideoStreamState frame)
        {
            base.NewFrameReceived(frame);

            if (frame.Data is VideoFragment)
            {
                var fragment = frame.Data as VideoFragment;
                if (fragment.SequenceNumber == 0)
                {
                    DisposeVideo();
                    PrepareToReceive();
                }

                HandleFragment(fragment);

                if (fragment.IsLast)
                {
                    AllReceived();
                    InitializeVideo();
                }
            }

            if (video != null)
            {
                switch (frame.State)
                {
                    case PlaybackState.Play:
                        video.Start();
                        break;
                    case PlaybackState.Stop:
                        video.Stop();
                        video.Rewind();
                        break;
                    case PlaybackState.Pause:
                        video.Pause();
                        break;
                }
            }
            State = frame.State;
        }

        /// <summary>
        /// Подготовить приёмник к закрытию.
        /// Освобождает все ресурсы, связанные с воспроизведением видео
        /// </summary>
        public override void Close()
        {
            base.Close();
            DisposeVideo();
            if (playFile != null)
            {
                CloseReceiveStream();
                File.Delete(playFileName);
            }
        }
        
        #endregion

        #region Private methods

        /// <summary>
        /// Подготовиться к воспроизведению полученного видео.
        /// </summary>
        private void InitializeVideo()
        {
            video = new DxPlay.DxPlay(ViewportPanel, playFileName);
            video.StopPlay += new DxPlay.DxPlay.DxPlayEvent(video_StopPlay);

            //чтобы на экране что-то отобразилось
            /*video.Start();
            video.Pause();*/
        }

        /// <summary>
        /// Освободить все ресурсы, связанные с воспроизведением видео.
        /// </summary>
        private void DisposeVideo()
        {
            if (video == null)
                return;

            video.StopPlay -= new DxPlay.DxPlay.DxPlayEvent(video_StopPlay);
            video.Dispose();
            video = null;
            if (File.Exists(playFileName))
                File.Delete(playFileName);
        }

        /// <summary>
        /// Вызывается компонентом video в случае достижения конца видео файла при
        /// воспроизведении
        /// </summary>
        /// <param name="sender">-</param>
        private void video_StopPlay(object sender)
        {
            State = PlaybackState.Stop;
            video.Rewind();
            video.Stop();
        }

        /// <summary>
        /// Подготовиться к приёму видео по фрагментам
        /// </summary>
        private void PrepareToReceive()
        {
            playFileName = Path.GetTempFileName();
            playFile = new FileStream(playFileName, FileMode.Create);

            progressBar = new CoolProgressBar();
            ViewportPanel.Controls.Add(progressBar);
            progressBar.Dock = DockStyle.Fill;
        }

        /// <summary>
        /// Дописать полученный фрагмент в файл
        /// </summary>
        /// <param name="videoFragment">Полученный фрагмент</param>
        private void HandleFragment(VideoFragment videoFragment)
        {
            playFile.Write(videoFragment.Data,
                0,
                videoFragment.Data.Length);

            if (progressBar.MaxValue != videoFragment.TotalFragments)
                progressBar.MaxValue = videoFragment.TotalFragments;
            progressBar.Progress = videoFragment.SequenceNumber;
        }

        /// <summary>
        /// Сделать всё, что необходимо, когда закончили получать фрагменты
        /// </summary>
        private void AllReceived()
        {
            CloseReceiveStream();

            ViewportPanel.Controls.Remove(progressBar);
            progressBar.Dispose();
            progressBar = null;
        }

        /// <summary>
        /// Закрыть файловый поток для получения.
        /// </summary>
        private void CloseReceiveStream()
        {
            playFile.Close();
            playFile = null;
        }

        #endregion
    }
}
